package besta.moon.engine;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.sql.*;

import besta.moon.MoonConnection;
import besta.moon.engine.store.CreateFile;
import besta.moon.engine.table.Column;
import besta.moon.engine.table.Columns;
import besta.moon.MoonException;
import besta.moon.MoonCode;
import besta.moon.util.MoonUtils;

public abstract class View {

    public static final int MAGIC_TABLE = 'S' << 24 | 'Q' << 16 | 'L' << 8 | 'T';
    public static final int MAGIC_VIEW = 'S' << 24 | 'Q' << 16 | 'L' << 8 | 'V';
    public static final int TABLE_VIEW_VERSION = 2;
    public static final int TABLE_VIEW_OLD_VERSION = 1;
    public final String name;
    public final Columns columns;
    private final long timestamp = System.currentTimeMillis();
    public static final int LOCK_NONE = 0;
    public static final int LOCK_INSERT = 1; 
    public static final int LOCK_READ = 2; 
    public static final int LOCK_WRITE = 3; 
    public static final int LOCK_TAB = 4; 

    protected View(String name, Columns columns) {
        this.name = name;
        this.columns = columns;
    }

    public static View load(MoonConnection con, Database database, String name) throws SQLException {
        FileChannel raFile = null;
        try {
            String fileName = MoonUtils.createTableViewFileName(database, name);
            File file = new File(fileName);
            if (!file.exists()) {
                throw MoonException.create(MoonCode.TABLE_OR_VIEW_MISSING, name);
            }
            raFile = MoonUtils.openRaFile(file, database.isReadOnly());
            ByteBuffer buffer = ByteBuffer.allocate(8);
            raFile.read(buffer);
            buffer.position(0);
            int magic = buffer.getInt();
            int version = buffer.getInt();
            switch (magic) {
                case MAGIC_TABLE, MAGIC_VIEW -> {
                }
                default -> throw MoonException.create(MoonCode.TABLE_OR_VIEW_FILE_INVALID, fileName);
            }
            if (version > TABLE_VIEW_VERSION) {
                throw MoonException.create(MoonCode.FILE_TOONEW, new Object[]{version, fileName});
            }
            if (version < TABLE_VIEW_OLD_VERSION) {
                throw MoonException.create(MoonCode.FILE_TOOOLD, new Object[]{version, fileName});
            }
            if (magic == MAGIC_TABLE) {
                return new Table(database, con, name, raFile, raFile.position(), version);
            }
            return new ViewTable(con, name, raFile, raFile.position());
        } catch (Exception e) {
            if (raFile != null) {
                try {
                    raFile.close();
                } catch (Exception e2) {
                    DriverManager.println(e2.toString());
                }
            }
            throw MoonException.createFromException(e);
        }
    }

    public File getFile(Database database) {
        return new File(MoonUtils.createTableViewFileName(database, name));
    }

    protected FileChannel createFile(MoonConnection con, Database database) throws Exception {
        if (database.isReadOnly()) {
            throw MoonException.create(MoonCode.DB_READONLY);
        }
        File file = getFile(database);
        boolean ok = file.createNewFile();
        if (!ok) {
            throw MoonException.create(MoonCode.TABLE_EXISTENT, name);
        }
        FileChannel raFile = MoonUtils.openRaFile(file, database.isReadOnly());
        con.add(new CreateFile(file, raFile, con, database));
        writeMagic(raFile);
        return raFile;
    }

    public abstract void writeMagic(FileChannel raFile) throws Exception;

    public String getName() {
        return name;
    }

    public long getTimestamp() {
        return timestamp;
    }

    public final int findColumnIdx(String columnName) {
        for (int i = 0; i < columns.size(); i++) {
            if (columns.get(i).getName().equalsIgnoreCase(columnName)) {
                return i;
            }
        }
        return -1;
    }

    public final Column findColumn(String columnName) {
        for (int i = 0; i < columns.size(); i++) {
            Column column = columns.get(i);
            if (column.getName().equalsIgnoreCase(columnName)) {
                return column;
            }
        }
        return null;
    }

    public void close() throws Exception {
    }

}
